package com.go.websocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.go.dto.JsonDtoWrapper;
import com.go.enums.ApiCode;
import com.go.websocket.dto.MessageInfo;
import com.go.websocket.dto.SocketUser;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ServerEndpoint 注解是一个类层次的注解，它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@Slf4j
@ServerEndpoint(value = "/mysocket/{userId}")
@Component
public class WebSocketService {

    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    // 若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
    public static Map<String, SocketUser> userMap = new ConcurrentHashMap<>();
    private static Map<String, String> sessionUID = new ConcurrentHashMap<>();

    private static Map<String, Object> heartMap = new HashMap<>();


    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public synchronized void onOpen(Session session, @PathParam("userId") String userId) {
        synchronized (sessionUID) {
            log.info("onOpen new userId：" + userId + "session id" + session.getId());
            getOnlineCount();
            SocketUser socketUser = new SocketUser();
            socketUser.setSession(session);
            socketUser.setUid(userId);
            userMap.put(socketUser.getUid(), socketUser);

            heartMap.put("code", 0);
            heartMap.put("msg", "心跳包响应");

            Set<String> keysuuid = sessionUID.keySet();
            for (String uuid : keysuuid) {
                if (sessionUID.get(uuid).equals(userId)) {
                    sessionUID.remove(uuid);
                }
            }
            sessionUID.put(session.getId(), socketUser.getUid());
            getOnlineCount();
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        synchronized (sessionUID) {
            String uid = sessionUID.get(session.getId());
            if (StringUtils.isEmpty(uid)) {
                uid = "nobody";
            }
            log.info("onClose-->" + uid);
            if (userMap.containsKey(uid)) {
                userMap.remove(uid);
                sessionUID.remove(session.getId());
                log.info("success remove on connect  from onclose=========================================>");
            }
            getOnlineCount();
        }
    }

    /**
     * 发生错误时调用
     * onError方法中不能处理与clientID、sessionID有关的逻辑,onError发生后会触发onClose方法
     *
     * @param session
     * @param error
     */
    @OnError
    public synchronized void onError(Session session, Throwable error) {
        /**
         * onError方法中不能处理与clientID、sessionID有关的逻辑
         */
        log.error("onError......................................");

    }


    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        String clientID = sessionUID.get(session.getId());
        log.info(clientID + ":onMessage" + message);
        if (StringUtils.isNotBlank(message) && message.charAt(0) == '{') {
            //解析发送的报文
            JSONObject jsonObject = JSON.parseObject(message);
            // 判断接收到的消息
            String msg = jsonObject.getString("msg");

            if ("heartbeat".equals(msg)) {
                try {
                    session.getBasicRemote().sendText(JSON.toJSONString(heartMap));
                } catch (Exception e) {
                    log.error("heart beat back to client:", e);
                    return;
                }

                if (!userMap.containsKey(clientID)) {
                    SocketUser socketUser = new SocketUser();
                    socketUser.setSession(session);
                    socketUser.setUid(clientID);
                    userMap.put(socketUser.getUid(), socketUser);
                }
            }
        } else {
            try {
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                log.error("common msg back client:", e);
            }
        }
    }


    public synchronized static int getOnlineCount() {
        Set<String> keys = userMap.keySet();
        log.info("当前在线人数为" + userMap.size());
        log.info("---start uid");
        for (String k : keys) {
            log.info(k);
        }
        log.info("---end uid");
        Set<String> setuuid = sessionUID.keySet();
        log.info("---start sessionid");
        for (String uuid : setuuid) {
            log.info("uuid" + uuid + sessionUID.get(uuid));
        }
        log.info("---end sessionid");

        return userMap.size();
    }

    public static JsonDtoWrapper sendMessage(MessageInfo messageInfo) {
        JsonDtoWrapper jo = new JsonDtoWrapper();
        log.info("sendMessage" + new Gson().toJson(messageInfo));
        try {
            SocketUser socketUser = userMap.get(messageInfo.getReceiveID());
            if (null != socketUser) {
                socketUser.getSession().getBasicRemote().sendText(messageInfo.getData());
            } else {
                jo.setCodeMsg(ApiCode.NOT_ONLINE);
                return jo;
            }
            jo.setCodeMsg(ApiCode.SUCCESS);
            return jo;
        } catch (Exception e) {
            log.error("socket sendMessage", e);
        }
        return jo;
    }
}